/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package SnpDbHandling;

import SnpDbEntities.User;
import java.rmi.RemoteException;
import SnpDbHndInterfaces.*;
import java.sql.*;
import com.microsoft.sqlserver.jdbc.*;
import java.util.Properties;
import java.io.FileInputStream;
import java.io.IOException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;

/**
 * Class which implements DBConnectable interface and realizes it's functionality.
 * @author Łukasz 'byku' Guzik
 */
public class DBConnector extends UnicastRemoteObject implements
	DBConnectable
{
    private class ConnectedClient
    {
	private Connection con;
	private User connectedUser;
	private DBUsrHandling dbUsrHandler;
	private DBCatHandling dbCatHandler;
	private DBSnpHandling dbSnpHandler;

	public ConnectedClient()
	{
	    dbUsrHandler = null;
	}
    }
    private Properties dbProps;
    private ConcurrentHashMap<String, ConnectedClient> connectedClients;

    DBConnector() throws RemoteException, IOException
    {
	super();
	connectedClients = new ConcurrentHashMap<String, ConnectedClient>();
	loadDbProperties();
    }

    @Override
    public String connectAndAuth(String login, String pwdHash) throws
	    RemoteException
    {
	ConnectedClient connectedCli = new ConnectedClient();
	String sessionID = null;
	Random randSesion = new Random();

	try
	{
	    connectedCli.con = connectToDb();
	    connectedCli.connectedUser = getUserForAuth(connectedCli.con, login, pwdHash);
	    if(connectedCli.connectedUser != null
		    && connectedCli.connectedUser.isEnabled())
	    {
		sessionID = User.calculateSHA1(pwdHash + randSesion.nextInt());
		connectedClients.put(sessionID, connectedCli);
	    } else
	    {
		throw new Exception("Authorization unsuccesful!");
	    }
	} catch(SQLException e)
	{
	    if(connectedCli.con != null && !connectedCli.con.isClosed())
	    {
		connectedCli.con.close();
	    }
	    DBHndLogger.logException(Level.SEVERE, e);
	} catch(Exception e)
	{
	    if(connectedCli.con != null && !connectedCli.con.isClosed())
	    {
		connectedCli.con.close();
	    }
	    DBHndLogger.logException(Level.INFO, e);
	} finally
	{
	    return sessionID;
	}
    }

    @Override
    public void disconnect(String sessionID) throws RemoteException
    {
	ConnectedClient conCli = connectedClients.get(sessionID);

	if(conCli == null)
	    return;

	try
	{
	    if(conCli.con != null && !conCli.con.isClosed())
	    {
		conCli.con.close();
	    }
	} catch(SQLException e)
	{
	    DBHndLogger.logException(Level.SEVERE, e);
	}

	connectedClients.remove(sessionID);
    }

    @Override
    public DBUsrHandling getUserHandler(String sessionID) throws RemoteException
    {
	ConnectedClient conCli = connectedClients.get(sessionID);

	if(conCli == null)
	{
	    return null;
	}

	if(conCli.dbUsrHandler == null)
	{
	    conCli.dbUsrHandler = new DBUsrHandler(conCli.con, conCli.connectedUser);
	}

	return conCli.dbUsrHandler;
    }

    @Override
    public DBCatHandling getCatHandler(String sessionID) throws RemoteException
    {
	ConnectedClient conCli = connectedClients.get(sessionID);

	if(conCli == null)
	{
	    return null;
	}

	if(conCli.dbCatHandler == null)
	{
	    conCli.dbCatHandler = new DBCatHandler(conCli.con, conCli.connectedUser);
	}
	return conCli.dbCatHandler;
    }

    @Override
    public DBSnpHandling getSnpHandler(String sessionID) throws RemoteException
    {
	ConnectedClient conCli = connectedClients.get(sessionID);

	if(conCli == null)
	{
	    return null;
	}

	if(conCli.dbSnpHandler == null)
	{
	    conCli.dbSnpHandler = new DBSnpHandler(conCli.con, conCli.connectedUser);
	}
	return conCli.dbSnpHandler;
    }

    private void loadDbProperties() throws IOException
    {
	dbProps = new Properties();

	FileInputStream propsIn = new FileInputStream("database.properties");
	dbProps.load(propsIn);
	propsIn.close();
    }

    private Connection connectToDb() throws SQLException
    {
	String portNo;
	Connection con;
	SQLServerDataSource ds = new SQLServerDataSource();
	ds.setUser(dbProps.getProperty("user", ""));
	ds.setPassword(dbProps.getProperty("password", ""));
	ds.setServerName(dbProps.getProperty("server", ""));
	ds.setInstanceName(dbProps.getProperty("instance", ""));
	portNo = dbProps.getProperty("port", "1433");
	if(portNo.length() > 0)
	{
	    ds.setPortNumber(Integer.parseInt(portNo));
	}
	ds.setDatabaseName(dbProps.getProperty("db_name", ""));
	con = ds.getConnection();

	return con;
    }

    private User getUserForAuth(Connection con, String login, String pwdHash)
	    throws
	    SQLException
    {
	PreparedStatement stmt = con.prepareStatement("SELECT * FROM Users WHERE (Login = ?) AND (Password = ?);");
	stmt.setString(1, login);
	stmt.setString(2, pwdHash);
	ResultSet rs = stmt.executeQuery();
	/*Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
	ResultSet rs = stmt.executeQuery("SELECT * FROM Users WHERE (Login = '" + login + "') AND (Password = '" + pwdHash + "');");*/
	UserRecord usrRecord = null;

	if(rs.next())
	    usrRecord = UserRecord.getFromResultSet(rs);
	stmt.close();

	return usrRecord != null ? usrRecord.getUser() : null;
    }
}
